#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<iostream>
#include<algorithm>
#include<stack>

using namespace std;

//判断符号的优先级,数字越大优先级越高。
int priority(char ch)
{ 
    if(ch == '+' || ch == '-')  return 0;
    else if(ch == '*' || ch|| ch == '/')  return 1;
    else if(ch == '^')  return 2;
    else if(ch == '(' || ch == ')')  return 3;
}

//删除输入的字符串中的空格，以便于后面的操作。
string blankremoved(string & s)
{
	string ans = "";
	for(int i = 0; i < s.length(); i++)
	{
		if(s[i] != ' ')
			ans += s[i];
	}
	return ans;
}

//对于表达式中的所有连续数字用符号!框住，以便于区分负数、实现小数点及其他功能。
string transformer(string & s)
{
	if (isdigit(s[0]))//如果第一个字符是数字，则在开头添加!。
		s.insert(0, 1, '!');
	if (isdigit(s[s.length() - 1]))//如果最后一个字符是数字，则在末尾添加!。
		s += '!';
	for (int i = 1; i < s.length()-1; i++)
	{
		//给数字前面加上!。
		if (s[i] == '-' && s[i - 1] == '(')//在逻辑正确时，左括号后的负号代表数的大小，故在负号前添加!。
			s.insert(i, 1, '!');
		else if (isdigit(s[i]) && (s[i - 1] == '+' || s[i - 1] == '*' || s[i - 1] == '/' || s[i - 1] == '^' || s[i-1] == '('))
			s.insert(i, 1, '!');
		else if (isdigit(s[i]) && s[i - 1] == '-')
		{
			//若左括号后连着的数是负数，则由于前面负号前已经添加过!，故此时左括号的位置在i-3。
			//此时负号后、数字前的位置无需再添加!。
			if (i >= 2 && s[i - 3] != '(')
				s.insert(i, 1, '!');
		}
		//给数字后面加上!。
		if (isdigit(s[i]) && (s[i + 1] == '+' || s[i + 1] == '-' || s[i + 1] == '*' || s[i + 1] == '/' || s[i + 1] == '^' || s[i + 1] == ')'))
			s.insert(i + 1, 1, '!');
	}
	return s;
}  

//将中缀表达式转后缀表达式。(判断符号!保留)
string transition(string str)
{
	int num = 0;
	stack<char>stk;//专门用于存储运算符。
	string output = "";
	int flag = 0;//在碰到'('后，由于'('优先级最高，故需要通过flag变量来抵消掉'('的最高优先级，
	//如果出现过'('，就要把flag变为1，然后在'('后面的运算符入栈之后，将flag变为0，后面才可以正常比较运算符优先级。
	for (int i = 0; i < str.length(); i++)
	{
		if (str[i] == '!')
			output += str[i];
		else if (str[i] == '.')
			output += '.';
		else if (isdigit(str[i]))
			output += str[i];
		else if (str[i] == '-' && str[i - 1] == '!' && (str[i - 2] == '!' || str[i - 2] == '('))//若减号前面有个!，则它可能代表负数，此时前面是左括号或!，并将负号直接放入输出中。
			output += '-';
		else if(str[i] != '('&& str[i] != ')')//字符为除括号外的运算符。
		{
			if (stk.empty())
				stk.push(str[i]);
			else if (flag == 1)//栈顶元素为左括号，优先级最大。
			{
				num--;
				stk.push(str[i]);
				flag = 0;
			}
			else if (priority(str[i]) > priority(stk.top()) || str[i]=='^' )//优先级比栈顶符号高，且由于幂运算顺序从右向左。
				stk.push(str[i]);
			else if ((priority(str[i]) <= priority(stk.top())) && (str[i]!='^'))//优先级不大于栈顶元素，且由于幂运算顺序从右向左。
			{
				output += stk.top();
				stk.pop();
				stk.push(str[i]);
			}
		}
		else//由于已经确保表达式合法，所以'(' ')'成对出现
		{
			if (str[i] == '(')//左括号入栈。
			{
				flag = 1;
				num++;
				stk.push(str[i]);
			}
			else if(str[i] == ')')//右括号与左括号抵消，将左括号之前的元素全部输出。
			{
				if (num > 0) {
					flag = 1;//右括号出现之后同样需要将flag变为1。
				}
				while (stk.top() != '(')
				{
					output += stk.top();
					stk.pop();
				}
				stk.pop();//让'('出栈，实现括号抵消。
			}
		}
	}
	while (!stk.empty())//最后剩下的运算符依次输出。
	{
		output += stk.top();
		stk.pop();
	}
	return output;
}

//将后缀表达式输出结果。
double getResult(string s)
{
	stack<double>result;//用于暂时存储数的栈。
	int j = 0;
	int num = 0;//num用来记录!的个数。
	for (int i = 0; i < s.length(); i=j)
	{
		if (s[i]=='!')
		{
			if (num % 2 == 0)//个数为偶数个，说明刚好读完整个数，存储这个数并放入栈中。
			{
				num++;
				int pos = i;//pos+1为这个数的开头数位的下标。
				j = pos + 1;
				for (; j < s.length(); j++)
				{
					if (s[j] == '!')
					{
						result.push(stod(s.substr(pos + 1, j - pos - 1)));//利用stod函数将字符串转化成数。
						break;
					}
				}
			}
			else//个数为奇数个，说明后面正要读到一个数。
			{
				num++;
				j++;
			}
		}
		else if (s[i] == '+')//若读到运算符，则直接对于栈顶的两个元素进行运算，并将结果放入栈中。
		{
			double temp1 = result.top();
			result.pop();
			double temp2 = result.top();
			result.pop();
			result.push(temp1 + temp2);
			j++;
		}
		else if (s[i] == '-')
		{
			double temp1 = result.top();
			result.pop();
			double temp2 = result.top();
			result.pop();
			result.push(temp2 - temp1);
			j++;
		}
		else if (s[i] == '*')
		{
			double temp1 = result.top();
			result.pop();
			double temp2 = result.top();
			result.pop();
			result.push(temp2 * temp1);
			j++;
		}
		else if (s[i] == '/')
		{
			double temp1 = result.top();
			result.pop();
			double temp2 = result.top();
			result.pop();
			if(temp1 == 0)//除数为0的异常处理。
			{
				throw "There exists a divisor which is zero!";
				break;
			}
			result.push(temp2 / temp1);
			j++;
		}
		else if (s[i] == '^')
		{
			double temp1 = result.top();
			result.pop();
			double temp2 = result.top();
			result.pop();
			result.push(pow(temp2 , temp1));		
			j++;
		}
	}
	return result.top();//最后返回栈顶元素。
}


//异常情况检测：检查括号是否平衡
bool check1(string str)
{	
	stack<char>stk1;
	for (int i = 0; i < str.length(); i++)
	{
		if (str[i] == '(')
			stk1.push(str[i]);
		else if (str[i] == ')')
		{
			if (stk1.empty() || stk1.top() != '(')
			{
				return false;
			}
			stk1.pop();
		}
	}
	if (!stk1.empty())
		return false;
	else
		return true;
}

//异常情况检测：检查运算符之间连接问题和运算符与数字之间的连接问题+-*/().^
bool check2(string str)
{ 
	int i;
	//表达式开头为运算符，则输出false。
	if (str[0] == '+' || str[0] == '-' || str[0] == '*' || str[0] == '/' || str[0] == ')' || str[0] == '^' || str[0] == '.')
		return false;
	for (i = 1; i < str.length(); i++)
	{
		if (isdigit(str[i]))//数字前面绝对不能有')'，后面绝对不能有'('，否则输出false。
		{
			if (str[i - 1] == ')')
				return false;
			if (str[i + 1] == '(')
				return false;
		}
		else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/')
		{
			if ((str[i] == '+' || str[i] == '*' || str[i] == '/') && (str[i - 1] == '('))//负数前面可以有(，其余符号前面有(时输出false。
				return false;
			//运算符前面不能也是运算符，除非^和(连接，否则输出false。
			if (str[i - 1] == '+' || str[i - 1] == '-' || str[i - 1] == '*' || str[i - 1] == '/' || str[i - 1] == '.' || str[i - 1] == '^')
				return false;
			//运算符后面不能是除了右括号的运算符，否则输出false。
			if (!isdigit(str[i+1]) && str[i+1]!='(')
				return false;
		}
		else if (str[i] == '(' || str[i] == ')')//'('和')'的部分不合法情况在之前判断过了，此处只考虑与别的括号、小数点和取幂符号的连接。
		{
			if (str[i - 1] == '.')//括号前不能是小数点。
				return false;
			//左括号后不能是.和^，右括号前不能是.和^，否则输出false。
			if ((str[i] == '(') && (str[i + 1] == '.' || str[i + 1] == '^'))
				return false;
			if ((str[i] == ')') && (str[i + 1] == '.' || str[i-1] == '('))
				return false;
		}
		else if (str[i] == '.')//'.'的部分不合法情况在之前判断过了，此处只考虑与别的.和^的连接。
		{
			//小数点前后不能是别的.和^，否则输出false。
			if (str[i - 1] == '.' || str[i-1] == '^')
				return false;
			if (str[i + 1] == '.' || str[i+1] == '^')
				return false;
		}
		else if (str[i] == '^')//此处只考虑与别的^的连接。
		{
			if (str[i - 1] == '^')
				return false;
		}
	}
	if(i == str.length())  return true;
}

#endif